﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ControlLibary
{
    public delegate void DataGridViewCheckBoxCheckChanged(object sender, CheckState state);

    public class CheckBoxColumn : DataGridViewTextBoxColumn
    {
        public CheckBoxColumn()
        {
            DataGridViewCheckboxHeaderCell headerCell = new DataGridViewCheckboxHeaderCell();

            this.HeaderCell = headerCell;

            headerCell.CheckChanged += headerCell_CheckChanged;

            CheckCell tempCell = new CheckCell();

            ReadOnly = true;

            this.CellTemplate = tempCell;

            this.SortMode = DataGridViewColumnSortMode.NotSortable;

        }

       
        private void headerCell_CheckChanged(object sen, CheckState e)
        {
            switch (e)
            {
                case CheckState.Checked:
                    foreach (DataGridViewRow row in this.DataGridView.Rows)
                    {
                        (row.Cells[this.Index] as CheckCell).CheckState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal;
                        this.DataGridView.InvalidateCell(row.Cells[this.Index]);
                    }
                    break;
                case CheckState.Indeterminate:
                    break;
                case CheckState.Unchecked:
                    foreach (DataGridViewRow row in this.DataGridView.Rows)
                    {
                        (row.Cells[this.Index] as CheckCell).CheckState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
                        this.DataGridView.InvalidateCell(row.Cells[this.Index]);
                    }
                    break;
                default:
                    break;
            }
        }
    }

    public class DataGridViewCheckboxHeaderCell : DataGridViewColumnHeaderCell
    {
        public event DataGridViewCheckBoxCheckChanged CheckChanged;

        Point checkBoxLocation;

        Size checkBoxSize;

        Point _cellLocation = new Point();

        System.Windows.Forms.VisualStyles.CheckBoxState _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;

        public System.Windows.Forms.VisualStyles.CheckBoxState CheckState
        {
            get { return _cbState; }
            set
            {
                if (value == System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal)
                    this.Value = true;
                else
                    this.Value = false;

                _cbState = value;
            }
        }

        //绘制列头checkbox
        protected override void Paint(System.Drawing.Graphics graphics,
           System.Drawing.Rectangle clipBounds,
           System.Drawing.Rectangle cellBounds,
           int rowIndex,
           DataGridViewElementStates dataGridViewElementState,
           object value,
           object formattedValue,
           string errorText,
           DataGridViewCellStyle cellStyle,
           DataGridViewAdvancedBorderStyle advancedBorderStyle,
           DataGridViewPaintParts paintParts)
        {
            base.Paint(graphics, clipBounds, cellBounds, rowIndex,
                dataGridViewElementState, value,
                formattedValue, errorText, cellStyle,
                advancedBorderStyle, paintParts);

            Point p = new Point();

            Size s = CheckBoxRenderer.GetGlyphSize(graphics, System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);

            p.X = cellBounds.Location.X + (cellBounds.Width / 2) - (s.Width / 2) - 1;//列头checkbox的X坐标
            p.Y = cellBounds.Location.Y + (cellBounds.Height / 2) - (s.Height / 2);//列头checkbox的Y坐标

            _cellLocation = cellBounds.Location;

            checkBoxLocation = p;

            checkBoxSize = s;

            CheckBoxRenderer.DrawCheckBox(graphics, checkBoxLocation, _cbState);
        }

        protected override void OnMouseDown(DataGridViewCellMouseEventArgs e)
        {
            Point p = new Point(e.X + _cellLocation.X, e.Y + _cellLocation.Y);
            if (p.X >= checkBoxLocation.X && p.X <=
                checkBoxLocation.X + checkBoxSize.Width
            && p.Y >= checkBoxLocation.Y && p.Y <=
                checkBoxLocation.Y + checkBoxSize.Height)
            {
                if (_cbState == System.Windows.Forms.VisualStyles.CheckBoxState.CheckedHot || _cbState == System.Windows.Forms.VisualStyles.CheckBoxState.CheckedPressed || _cbState == System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal)
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed;
                else if (_cbState == System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedHot || _cbState == System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed || _cbState == System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal)
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedPressed;
                else
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed;

                this.DataGridView.InvalidateCell(this);

                if (CheckChanged != null)
                {
                    CheckState state = (_cbState == System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedHot || _cbState == System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed)
                        ? System.Windows.Forms.CheckState.Unchecked
                        : System.Windows.Forms.CheckState.Checked;
                    CheckChanged(this, state);
                }
            }
            base.OnMouseDown(e);
        }


        protected override void OnMouseMove(DataGridViewCellMouseEventArgs e)
        {
            Point p = new Point(e.X + _cellLocation.X, e.Y + _cellLocation.Y);
            if (p.X >= checkBoxLocation.X && p.X <=
                checkBoxLocation.X + checkBoxSize.Width
            && p.Y >= checkBoxLocation.Y && p.Y <=
                checkBoxLocation.Y + checkBoxSize.Height)
            {
                switch (_cbState)
                {
                    case System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedHot;
                        this.DataGridView.InvalidateCell(this);
                        break;
                    case System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedHot;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    case System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedHot;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    default:
                        break;
                }
            }
            else
            {
                switch (_cbState)
                {
                    case System.Windows.Forms.VisualStyles.CheckBoxState.CheckedHot:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;
                    case System.Windows.Forms.VisualStyles.CheckBoxState.CheckedPressed:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    case System.Windows.Forms.VisualStyles.CheckBoxState.MixedHot:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    case System.Windows.Forms.VisualStyles.CheckBoxState.MixedPressed:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    case System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedHot:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;
                    case System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    default:
                        break;
                }
            }

            base.OnMouseMove(e);
        }

        protected override void OnMouseLeave(int rowIndex)
        {
            base.OnMouseLeave(rowIndex);

            switch (_cbState)
            {
                case System.Windows.Forms.VisualStyles.CheckBoxState.CheckedHot:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.VisualStyles.CheckBoxState.CheckedPressed:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.VisualStyles.CheckBoxState.MixedHot:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.VisualStyles.CheckBoxState.MixedPressed:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedHot:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                default:
                    break;
            }
        }


        public void ChangedState(System.Windows.Forms.CheckState state)
        {
            switch (state)
            {
                case System.Windows.Forms.CheckState.Checked:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal;
                    this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.CheckState.Indeterminate:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal;
                    this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.CheckState.Unchecked:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
                    this.DataGridView.InvalidateCell(this);
                    break;
                default:
                    break;
            }
        }

        internal void StateChanged()
        {
            CheckCell cell = this.DataGridView.Rows[0].Cells[this.ColumnIndex] as CheckCell;

            bool flag = cell.Value == null ? false : (bool)(cell.Value);

            System.Windows.Forms.CheckState state = flag ? System.Windows.Forms.CheckState.Checked : System.Windows.Forms.CheckState.Unchecked;

            for (int i = 1; i < this.DataGridView.Rows.Count; i++)
            {
                cell = this.DataGridView.Rows[i].Cells[this.ColumnIndex] as CheckCell;
                bool _flag = cell.Value == null ? false : (bool)(cell.Value);

                if (flag != _flag)
                {
                    state = System.Windows.Forms.CheckState.Indeterminate;
                    break;
                }
            }
            ChangedState(state);
        }
    }


    public class CheckCell : DataGridViewTextBoxCell
    {
        public CheckCell() { this._cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal; }

        Point checkBoxLocation;

        Size checkBoxSize;

        Point _cellLocation = new Point();

        System.Windows.Forms.VisualStyles.CheckBoxState _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;

        public System.Windows.Forms.VisualStyles.CheckBoxState CheckState
        {
            get { return _cbState; }
            set
            {
                if (value == System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal)
                    this.Value = true;
                else
                    this.Value = false;

                _cbState = value;
            }
        }


        protected override bool SetValue(int rowIndex, object value)
        {
            if (base.SetValue(rowIndex, value))
            {
                if (value != null)

                    if ((bool)value)
                    {
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal;
                        ToolTipText = "选择";
                    }
                    else
                    {
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
                        ToolTipText = "未选择";
                    }
                return true;
            }
            return false;
        }

        //绘制列头checkbox
        protected override void Paint(System.Drawing.Graphics graphics,
           System.Drawing.Rectangle clipBounds,
           System.Drawing.Rectangle cellBounds,
           int rowIndex,
           DataGridViewElementStates cellState,
           object value,
           object formattedValue,
           string errorText,
           DataGridViewCellStyle cellStyle,
           DataGridViewAdvancedBorderStyle advancedBorderStyle,
           DataGridViewPaintParts paintParts)
        {
            base.Paint(graphics, clipBounds, cellBounds, rowIndex,
                cellState, null,
                null, null, cellStyle,
                advancedBorderStyle, paintParts);

            Point p = new Point();

            Size s = CheckBoxRenderer.GetGlyphSize(graphics, System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);

            p.X = cellBounds.Location.X + (cellBounds.Width / 2) - (s.Width / 2) - 1;//列头checkbox的X坐标
            p.Y = cellBounds.Location.Y + (cellBounds.Height / 2) - (s.Height / 2);//列头checkbox的Y坐标

            _cellLocation = cellBounds.Location;

            checkBoxLocation = p;

            checkBoxSize = s;

            CheckBoxRenderer.DrawCheckBox(graphics, checkBoxLocation, _cbState);
        }
         
        protected override void OnMouseDown(DataGridViewCellMouseEventArgs e)
        {
            Point p = new Point(e.X + _cellLocation.X, e.Y + _cellLocation.Y);
            if (p.X >= checkBoxLocation.X && p.X <=
                checkBoxLocation.X + checkBoxSize.Width
            && p.Y >= checkBoxLocation.Y && p.Y <=
                checkBoxLocation.Y + checkBoxSize.Height)
            {
                if (_cbState == System.Windows.Forms.VisualStyles.CheckBoxState.CheckedHot || _cbState == System.Windows.Forms.VisualStyles.CheckBoxState.CheckedPressed || _cbState == System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal)
                {
                    Value = false;
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed;
                }
                else if (_cbState == System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedHot || _cbState == System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed || _cbState == System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal)
                {
                    Value = true;
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedPressed;
                }
                else
                {
                    Value = false;
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed;
                }

                if (this.DataGridView.Rows[this.RowIndex].Cells[0] is GroupCell)
                {
                    GroupCell groupcell = this.DataGridView.Rows[this.RowIndex].Cells[0] as GroupCell;
                    if (groupcell.ChildCells != null && groupcell.ChildCells.Length > 0)
                    {
                        System.Windows.Forms.CheckState state = (bool)Value ? System.Windows.Forms.CheckState.Checked : System.Windows.Forms.CheckState.Unchecked;
                        foreach (GroupCell cell in groupcell.ChildCells)
                        {
                            (this.DataGridView.Rows[cell.RowIndex].Cells[this.ColumnIndex] as CheckCell).ChangedState(state);
                        }
                    }
                    else
                    {
                        if (groupcell.ParentCell != null)
                        {
                            bool flag = this.Value == null ? false : (bool)(this.Value);

                            System.Windows.Forms.CheckState state = flag ? System.Windows.Forms.CheckState.Checked : System.Windows.Forms.CheckState.Unchecked;

                            foreach (GroupCell cell in groupcell.ParentCell.ChildCells)
                            {
                                CheckCell _cell = this.DataGridView.Rows[cell.RowIndex].Cells[this.ColumnIndex] as CheckCell;

                                _cell = this.DataGridView.Rows[cell.RowIndex].Cells[this.ColumnIndex] as CheckCell;

                                bool _flag = _cell.Value == null ? false : (bool)(_cell.Value);

                                if (flag != _flag)
                                {
                                    state = System.Windows.Forms.CheckState.Indeterminate;
                                    break;
                                }
                            }

                            (this.DataGridView.Rows[groupcell.ParentCell.RowIndex].Cells[this.ColumnIndex] as CheckCell).ChangedState(state);
                        }
                    }
                }

                this.DataGridView.InvalidateCell(this);

                (this.OwningColumn.HeaderCell as DataGridViewCheckboxHeaderCell).StateChanged();

            }
            base.OnMouseDown(e);
        }


        protected override void OnMouseMove(DataGridViewCellMouseEventArgs e)
        {
            Point p = new Point(e.X + _cellLocation.X, e.Y + _cellLocation.Y);
            if (p.X >= checkBoxLocation.X && p.X <=
                checkBoxLocation.X + checkBoxSize.Width
            && p.Y >= checkBoxLocation.Y && p.Y <=
                checkBoxLocation.Y + checkBoxSize.Height)
            {
                switch (_cbState)
                {
                    case System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedHot;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    case System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedHot;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    case System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedHot;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    default:
                        break;
                }
            }
            else
            {
                switch (_cbState)
                {
                    case System.Windows.Forms.VisualStyles.CheckBoxState.CheckedHot:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;
                    case System.Windows.Forms.VisualStyles.CheckBoxState.CheckedPressed:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    case System.Windows.Forms.VisualStyles.CheckBoxState.MixedHot:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    case System.Windows.Forms.VisualStyles.CheckBoxState.MixedPressed:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    case System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedHot:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;
                    case System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed:
                        _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
                        this.DataGridView.InvalidateCell(this);
                        break;

                    default:
                        break;
                }
            }

            base.OnMouseMove(e);
        }

        protected override void OnMouseLeave(int rowIndex)
        {
            base.OnMouseLeave(rowIndex);

            switch (_cbState)
            {
                case System.Windows.Forms.VisualStyles.CheckBoxState.CheckedHot:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.VisualStyles.CheckBoxState.CheckedPressed:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.VisualStyles.CheckBoxState.MixedHot:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.VisualStyles.CheckBoxState.MixedPressed:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedHot:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal; this.DataGridView.InvalidateCell(this);
                    break;
                default:
                    break;
            }
        }

        internal void ChangedState(System.Windows.Forms.CheckState state)
        {
            switch (state)
            {
                case System.Windows.Forms.CheckState.Checked:
                    this.Value = true;
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal;
                    this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.CheckState.Indeterminate:
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal;
                    this.DataGridView.InvalidateCell(this);
                    break;
                case System.Windows.Forms.CheckState.Unchecked:
                    this.Value = false;
                    _cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
                    this.DataGridView.InvalidateCell(this);
                    break;
                default:
                    break;
            }
        }
    }
}
